package com.jaws.core.common.xxljob.executor;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.jaws.core.common.constant.JawsFrameworkCoreConstant;
import com.jaws.core.common.vo.OpResponse;
import com.jaws.core.common.xxljob.annotation.JawsJobHandlerRegistrar;
import com.jaws.core.common.xxljob.domain.XxlJobGroup;
import com.jaws.core.common.xxljob.domain.XxlJobInfo;
import com.jaws.core.compatible.util.HttpUtil;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.handler.IJobHandler;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

/**
 * xxl-job 执行器 自定义
 * 参考 XxlJobExecutor
 *
 * @author liangqm
 * @date 2018-07-23 19:14:18
 * @since v1.0.3
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class JawsJobExecutor extends XxlJobExecutor {

	private static Logger logger = LoggerFactory.getLogger(JawsJobExecutor.class);

	private static String groupId;

	@Value("${jaws.job.executor.title}")
	private String groupTitle;

	@Value("${jaws.job.admin.addresses}")
	private String adminAddresses;

	@Value("${jaws.job.executor.appname}")
	private String appName;

	@Value("${jaws.job.executor.ip}")
	private String ip;

	@Value("${jaws.job.executor.port}")
	private int port;

	@Value("${jaws.job.accessToken}")
	private String accessToken;

	@Value("${jaws.job.executor.logpath}")
	private String logPath;

	@Value("${jaws.job.executor.logretentiondays}")
	private int logRetentionDays;

	@Override
	public void start() {
		// 1 启动执行器
		startExecutor();

		// 2 自动注册Job到控制台
		registerJobToAdmin();
	}


	/**
	 * 自动注册job 到 admin
	 *
	 * @author liangqm
	 * @date 2018-07-20 16:48:18
	 * @since v1.0.3
	 */
	private void registerJobToAdmin() {
		Set<Map.Entry<String, XxlJobInfo>> entries = JawsJobHandlerRegistrar.getJawsJobInfoMap().entrySet();

		for (Map.Entry<String, XxlJobInfo> entry : entries) {

			XxlJobInfo xxlJobInfo = entry.getValue();
			// 1. 获取执行器ID
			xxlJobInfo.setJobGroup(obtainJobExecutorId(this.appName));

			// 2. 注 xxl job
			Method executeMethod = xxlJobInfo.getJobExecuteMethod();
			Object jobHandler = XxlJobExecutor.getApplicationContext().getBean(xxlJobInfo.getJobHandlerClass());

			registJobHandler(xxlJobInfo.getExecutorHandler(), new IJobHandler() {
				@Override
				public ReturnT<String> execute(String param) {
					try {
						executeMethod.invoke(jobHandler, param);
					} catch (Exception e) {
						return new ReturnT<>(ReturnT.FAIL_CODE, JSON.toJSONString(e));
					}
					return ReturnT.SUCCESS;
				}
			});
			autoRegisterJobToAdmin(xxlJobInfo);
		}
	}


	/**
	 * 启动xxl job 执行器，注册到xxl job 控制台
	 *
	 * @author liangqm
	 * @date 2018-07-20 15:42
	 * @since v1.0.3
	 */
	private void startExecutor() {
		// 调用xxl job 执行器 , 自动注册到 xxl job admin, 基于 jetty 服务
		super.setAdminAddresses(adminAddresses);
		super.setAppName(appName);
		super.setIp(ip);
		super.setPort(port);
		super.setAccessToken(accessToken);
		super.setLogPath(logPath);
		super.setLogRetentionDays(logRetentionDays);
		try {
			super.start();
		} catch (Exception e) {
			throw new RuntimeException(String.format("启动 jaws job 执行器异常: %s", JSON.toJSONString(e)));
		}
	}

	/**
	 * 自动添加job 到 xxl admin
	 *
	 * @param xxlJobInfo jop信息
	 * @author liangqm
	 * @date 2018-07-20 10:45:18
	 * @since v1.0.3
	 */
	private void autoRegisterJobToAdmin(XxlJobInfo xxlJobInfo) {
		Map<String, String> params = (Map<String, String>) JSON.parse(JSON.toJSONString(xxlJobInfo));
		this.rpcRequest(JawsFrameworkCoreConstant.AUTO_REGISTER_JOB_URL, params);
	}

	/**
	 * 根据执行器app_name 获取groupId
	 *
	 * @param groupName 执行器名称
	 * @author liangqm
	 * @date 2018-07-19 20:10：18
	 * @since v1.0.3
	 */
	private String obtainJobExecutorId(String groupName) {

		if (StringUtils.isNotBlank(groupId)) {
			return groupId;
		}

		if (groupName.length() > JawsFrameworkCoreConstant.JAWS_GROUP_NAME_MAX_SIZE) {
			throw new Error(String.format("groupName 最大长度%s ，当前长度 %s", JawsFrameworkCoreConstant.JAWS_GROUP_NAME_MAX_SIZE, groupName.length()));

		}

		if (groupTitle.length() > JawsFrameworkCoreConstant.JAWS_GROUP_TITLE_MAX_SIZE) {
			throw new Error(String.format("groupTitle 最大长度%s ，当前长度 %s", JawsFrameworkCoreConstant.JAWS_GROUP_TITLE_MAX_SIZE, groupTitle.length()));
		}

		// 从控制心中获取 执行器ID
		XxlJobGroup xxlJobGroup = new XxlJobGroup();
		xxlJobGroup.setAppName(groupName);
		xxlJobGroup.setTitle(groupTitle);
		xxlJobGroup.setOrder("1");
		xxlJobGroup.setAddressType("0");
		Map<String, String> params = (Map<String, String>) JSON.parse(JSON.toJSONString(xxlJobGroup));

		groupId = this.rpcRequest(JawsFrameworkCoreConstant.OBTAIN_JOB_EXECUTOR_ID_URL, params);

		if (StringUtils.isBlank(groupId)) {
			throw new RuntimeException(String.format("获取执行器ID失败,执行器名称: %s", groupName));
		}

		return groupId;
	}


	/**
	 * HTTP 请求 xxl job 控制台
	 *
	 * @param requestPath 请求URL路径
	 * @return http response
	 * @author liangqm
	 * @date 2018-07-23 15:58:18
	 * @since v1.0.3
	 */
	private String rpcRequest(String requestPath, Map<String, String> params) {
		Map<String, String> headers = Maps.newHashMap();
		headers.put("JOB-HANDLER", "JOB-HANDLER");

		String[] adminAddressArray = this.adminAddresses.split(",");
		for (String adminAddress : adminAddressArray) {
			String accessUrl = adminAddress + requestPath;
			try {
				String rps = HttpUtil.post(accessUrl, params, headers);
				logger.info(String.format("请求路径:%s , 返回值:%s", accessUrl, rps));
				OpResponse opResponse = JSON.parseObject(rps, OpResponse.class);
				if (opResponse.getCode() == 0) {
					// 处理成功
					return opResponse.getContent().toString();
				} else {
					logger.error(String.format(" %s 网络调用失败: %s", accessUrl, opResponse.getDisplay()));
				}
			} catch (Exception e) {
				logger.warn(String.format(" %s 网络调用失败: %s ", accessUrl, JSON.toJSONString(e)));
			}
		}
		return "";
	}
}
